package com.bocloud.cmp.web.upload;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.common.enums.PostDataFormat;
import com.bocloud.common.http.HttpClient;
import com.bocloud.common.model.Result;

import io.fabric8.kubernetes.client.Callback;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.dsl.ExecWatch;
import io.fabric8.kubernetes.client.utils.InputStreamPumper;

@Component
public class TerminalServiceHandller implements WebSocketHandler {
	private static Logger logger = Logger.getLogger(TerminalServiceHandller.class);
	private static List<WebSocketSession> currentUsers;
	private static final String OPENSHIFT_PORT = ":8443";
	private static final String HTTPS_PROXY = "https://";
	private static String flag = "notFalg";
	private static final String SERVER_ACOCUNT_NAME = "/api/v1/namespaces/management-infra/serviceaccounts/management-admin";
	private static final String SERVER_ACOCUNT_TOKEN = "/api/v1/namespaces/management-infra/secrets/";

	/**
	 * @return the currentUsers
	 */
	public static List<WebSocketSession> getCurrentUsers() {
		return currentUsers;
	}

	/**
	 * @param currentUsers
	 *            the currentUsers to set
	 */
	public static void setCurrentUsers(List<WebSocketSession> currentUsers) {
		TerminalServiceHandller.currentUsers = currentUsers;
	}

	static {
		TerminalServiceHandller.setCurrentUsers(new ArrayList<WebSocketSession>());
	}

	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		TerminalServiceHandller.getCurrentUsers().add(session);
	}

	@Override
	public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
		 flag = "notFalg";
		//获取前端传输过来的信息
		TextMessage textMessage = (TextMessage) message;
		TerminalBean logBean = JSONObject.parseObject(textMessage.getPayload(), TerminalBean.class);
		if (!logBean.isFlag()) {
			connection(session, logBean);
		}else{
			exec(session, logBean);
		}
	        
	}
	/**
	 * 获取与pod容器的连接
	 * @param session
	 * @param logBean
	 */
	@SuppressWarnings("resource")
	public void connection(WebSocketSession session, TerminalBean logBean){
		try {
			/**
			 * 获取token
			 */
			String token = getToken(logBean.getUrl());
			//执行远程命令
			String master =HTTPS_PROXY + logBean.getUrl() + OPENSHIFT_PORT;
			Config config = new ConfigBuilder().withMasterUrl(master).build();
			config.setTrustCerts(true);
			config.setOauthToken(token);
			KubernetesClient client = new DefaultKubernetesClient(config);
			ExecWatch watch = client.pods().inNamespace(logBean.getNamespace()).withName(logBean.getResourceName()).inContainer(logBean.getContainerName())
			         .redirectingInput()
			         .redirectingOutput()
			         .exec();
			logger.info("开始进入容器...... ");
			logger.info("获取进入容器[" + logBean.getContainerName() + "]连接地址:" + watch.toString());
			logger.info("检查是否获取错误的连接信息:" + watch.getError());
			session.getAttributes().put("watch", watch);
			if ("notNormal".equals(logBean.getNotFalg())) {//当执行错误命令或执行不可执行的命令
				exec(session, logBean);
			}else{
				logBean.setFlag(true);
				logBean.setData(null);
				logBean.setNotFalg("normal");
				session.sendMessage(new TextMessage(JSONObject.toJSONString(logBean)));
			}
		} catch (IOException e) {
			logger.error("发送消息失败", e);
		}  
	}
	/**
	 * 在容器里执行命令
	 * @param session
	 * @param logBean
	 */
	public void exec(WebSocketSession session, TerminalBean logBean){
		class SystemOutCallback implements Callback<byte[]>{
	       	 @Override
	            public void call(byte[] data) {
	            	try {
	            		flag = "normal";
	            		logBean.setFlag(true);
	        			logBean.setData(new String(data));
	        			logBean.setNotFalg("normal");
	        			session.sendMessage(new TextMessage(JSONObject.toJSONString(logBean)));
	    			} catch (IOException e) {
	    				logger.error("发送消息失败", e);
	    			}
	            }
	       }
		ExecWatch watch = (ExecWatch) session.getAttributes().get("watch");
		StringBuffer command = new StringBuffer();
		command.append(logBean.getCommand()).append("\n");
		ExecutorService executorService = Executors.newSingleThreadExecutor();
		 try (
               InputStreamPumper pump = new InputStreamPumper(watch.getOutput(), new SystemOutCallback()))
	        {
	            executorService.submit(pump);
	            watch.getInput().write(command.toString().getBytes());
	            if (command.toString().contains("cd")) {
	            	flag = "normal";
	            	logBean.setFlag(true);
	    			logBean.setData(new String());
	    			logBean.setNotFalg("normal");
	    			session.sendMessage(new TextMessage(JSONObject.toJSONString(logBean)));
				}
	            Thread.sleep(3 * 1000);
	        } catch (Exception e) {
	        	logger.debug("connection ["+logBean.getResourceName()+"] Terminal perform command ["+logBean.getCommand()+"] Exception", e);
	            throw KubernetesClientException.launderThrowable(e);
	        } finally {
	        	if("notFalg".equals(flag)){
	        		try{
	        			logBean.setFlag(false);
		    			logBean.setData(new String(command+": not found or is error"));
		    			logBean.setNotFalg("notNormal");
		    			session.sendMessage(new TextMessage(JSONObject.toJSONString(logBean)));
	        		}catch (Exception e) {
	        			logger.error("发送消息失败", e);
					}
	        	}
	            executorService.shutdownNow();
	        }
	}
	
	public String getToken(String proxyUrl) {
		String tokenName = null;
		String url = null;
		Result message = null;
		JSONObject object = null;
		try {
			// 获取 token name
			url = HTTPS_PROXY + proxyUrl + OPENSHIFT_PORT + SERVER_ACOCUNT_NAME;
			message = getMessage(url);
			if (message.isFailed()) {
				logger.error("获取serviceAccount 的 token name 失败");
				return null;
			}
			object = JSONObject.parseObject(message.getMessage());
			JSONArray secrets = JSONObject.parseArray(object.get("secrets").toString());
			for (Object secret : secrets) {
				JSONObject parseObject = JSONObject.parseObject(secret.toString());
				Object name = parseObject.get("name");
				if (!name.toString().contains("token")) {
					tokenName = name.toString();
				}
			}
			// 获取token
			url = HTTPS_PROXY + proxyUrl + OPENSHIFT_PORT + SERVER_ACOCUNT_TOKEN + tokenName;
			message = getMessage(url);
			if (message.isFailed()) {
				logger.error("获取token信息失败");
				return null;
			}
			object = JSONObject.parseObject(message.getMessage());
			JSONObject metadata = JSONObject.parseObject(object.getString("metadata"));
			JSONObject annotations = JSONObject.parseObject(metadata.getString("annotations"));
			return annotations.getString("openshift.io/token-secret.value");
		} catch (Exception e) {
			logger.error("获取token异常", e);
			return null;
		}
	}

	/**
	 * httpClient获取数据
	 * 
	 * @param url
	 * @return
	 */
	public Result getMessage(String url) {
		HttpClient httpClient = new HttpClient(1000 * 10);
		httpClient.setDataFormat(PostDataFormat.RAW);
		return httpClient.get(url);
	}

	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
		TerminalServiceHandller.getCurrentUsers().remove(session);
	}

	@Override
	public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
		if (session.isOpen()) {
			session.close();
		}
		TerminalServiceHandller.getCurrentUsers().remove(session);
	}

	@Override
	public boolean supportsPartialMessages() {
		return false;
	}
}
